package com.lagou.edu.factory;

import com.lagou.edu.annotation.Autowired;
import com.lagou.edu.annotation.Service;
import com.lagou.edu.annotation.Transactional;

import java.io.File;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.LinkedBlockingQueue;

public class AnnotationBeanFactory {

    private static Map<String, Object> beanMap = null;

    public synchronized static void initBeanFactory() {
        try {
            System.out.println("initializing annotation bean map begin");
            //扫描包
            Map<String, Object> classMap = scanPackage("com.lagou.edu");
            //获取注解类
            beanMap = getAnnotationBeans(classMap);
            // 依赖注入
            dependencyInjection(beanMap);
            // 代理事物
            proxyTransaction(beanMap);

            System.out.println("initializing annotation bean map finished, beans count: " + beanMap.size());
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println(e.getMessage());
        }
    }

    /**
     * 根据class获得bean
     */
    public static <T> T getBean(Class<?> classInfo) {
        synchronized (AnnotationBeanFactory.class) {
            if (beanMap == null) {
                initBeanFactory();
            }
        }
        String beanId = getAnnotationBeanId(classInfo);
        return (T) beanMap.get(beanId);
    }

    /**
     * 根据beanId获得bean
     */
    public static <T> T getBean(String beanId) {
        synchronized (AnnotationBeanFactory.class) {
            if (beanMap == null) {
                initBeanFactory();
            }
        }
        return (T) beanMap.get(beanId);
    }

    /**
     * 获取类的 BeanId，如果注解声明，就获取注解的，如果没有，就是classname
     *
     * @param clazz
     * @return
     */
    private static String getAnnotationBeanId(Class<?> clazz) {
        String beanId = "";
        if (clazz.isAnnotationPresent(Service.class)) {
            Annotation serviceAnnotation = clazz.getAnnotation(Service.class);
            beanId = ((Service) serviceAnnotation).value();
        }
        if (beanId.equals("")) {
            beanId = clazz.getSimpleName();
        }
        return beanId;
    }

    public static void main(String[] args) {
//        String packageName = "com.lagou.edu";
//        String scanPath = packageName.replace(".", "/");
//        System.out.println("scanPath:" + scanPath);
//        URL rootUrl = AnnotationBeanFactory.class.getClassLoader().getResource("");
//        String rootPath = rootUrl.getPath();
//        System.out.println("filePath:" + rootPath + scanPath);
//
//        File file5 = new File(rootPath + scanPath);
//        System.out.println(file5.listFiles().toString());
//        for (File f : file5.listFiles()) {
//            System.out.println(f.getName());
//        }
//
//        String rootPath2 = AnnotationBeanFactory.class.getClassLoader().getResource("").getPath();
//        String filePath = file5.getAbsolutePath();
//        String rootAbsolutePath = rootPath2.replace("/","\\").substring(1);
//        System.out.println("rootPath2 : "+rootPath2);
//        System.out.println("filePath : "+filePath);
//        System.out.println("rootAbsolutePath : "+rootAbsolutePath);


        try {
            initBeanFactory();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    /**
     * 扫描包路径下的所有类，通过反射实例化对象，然后保存在hashmap中
     */
    private static Map<String, Object> scanPackage(String packageName) throws Exception {
        Map<String, Object> classMap = new HashMap<>();
        List<String> nameList = getAllClasses(packageName.replace(".", "/"));
        if (nameList.size() > 0) {
            // 获取 所有类的类全名
            for (int i = 0; i < nameList.size(); i++) {
                String name = nameList.get(i);
                // 通过反射技术实例化对象
                Class<?> clazzInfo = Class.forName(name);
                //注解,接口,enum:不需要加载到容器中
                if (!clazzInfo.isAnnotation() && !clazzInfo.isInterface() && !clazzInfo.isEnum()) {
                    // 初始化类对象 添加到容器中
                    if (!classMap.containsKey(name)) {
                        classMap.put(name, clazzInfo);
                    }
                }
            }
        } else {
            throw new Exception("路径不存在！");
        }
        return classMap;
    }

    /**
     * 扫描获得所有Class文件
     */
    private static List<String> getAllClasses(String scanPath) throws IOException {
        List<String> classList = new ArrayList<>();
        //获取项目的classes路径，得编译完才有
        String rootPath = AnnotationBeanFactory.class.getClassLoader().getResource("").getPath();
        //重新格式化rootPath，让它和file.getAbsolutePath()获得的路径格式相符
        String rootAbsolutePath = rootPath.replace("/","\\").substring(1);
        File scanPathFile = new File(rootPath + scanPath);
        //使用队列遍历目录文件
        Queue<File> dirs = new LinkedBlockingQueue<File>();
        dirs.add(scanPathFile);
        for (;;) {
            File nextFile = dirs.poll();
            for (File file : nextFile.listFiles()) {
                if (file.isDirectory()) {
                    //如果是目录，放入队列中，等下一次再 listFiles 获取文件
                    dirs.add(file);
                } else {
                    String filePath = file.getAbsolutePath();
                    //格式化，获得类的全限定名
                    String classPath = filePath.replace(rootAbsolutePath, "").replaceAll("\\\\", ".").replaceAll(".class", "");
                    classList.add(classPath);
                }
            }
            if (dirs.isEmpty()) {
                break;
            }
        }
        return classList;
    }

    /**
     * 分析扫描到的类，获取到 @Service 的类信息，保存在 beanMap 中 作为bean
     *
     * @param classMap
     * @throws Exception
     */
    private static Map<String, Object> getAnnotationBeans(Map<String, Object> classMap) throws Exception {
        Map<String, Object> beanMap = new HashMap<>();
        for (Map.Entry<String, Object> entry : classMap.entrySet()) {
            Class<?> clazzInfo = (Class<?>) entry.getValue();
            if (clazzInfo.isAnnotationPresent(Service.class)) {
                String beanId = getAnnotationBeanId((Class<?>) clazzInfo);
                beanMap.put(beanId, clazzInfo.getDeclaredConstructor().newInstance());
            }
        }
        return beanMap;
    }

    /**
     * 实现 @Autowired 依赖注入
     */
    private static void dependencyInjection(Map<String, Object> beanMap) throws IllegalAccessException {
        for (Map.Entry<String, Object> entry : beanMap.entrySet()) {
            Object object = entry.getValue();
            Class<?> clazz = object.getClass();
            Field[] fieldArr = clazz.getDeclaredFields();
            for (int i = 0; i < fieldArr.length; i++) {
                Field field = fieldArr[i];
                Annotation autowiredAnnotation = field.getAnnotation(Autowired.class);
                if (autowiredAnnotation != null) {
                    Class<?> declaringClass = field.getType();
                    for (Map.Entry<String, Object> entryInfo : beanMap.entrySet()) {
                        Object objectInfo = entryInfo.getValue();
                        if (declaringClass.isInstance(objectInfo)) {
                            field.setAccessible(true);
                            //设置属性的依赖关系
                            field.set(object, objectInfo);
                        }
                    }
                }
            }
        }
    }

    /**
     * 通过动态代理，实现 @Transactional 事务
     *
     * @throws Exception
     */
    private static void proxyTransaction(Map<String, Object> beanMap) throws Exception {
        for (Map.Entry<String, Object> entry : beanMap.entrySet()) {
            Object object = entry.getValue();
            Class<?> clazz = object.getClass();

            //获得类的所有方法
            Method[] methodArr = clazz.getDeclaredMethods();
            for (int i = 0; i < methodArr.length; i++) {
                Method method = methodArr[i];
                Annotation tractionAnnotation = method.getAnnotation(Transactional.class);
                if (tractionAnnotation != null) {
                    String beanId = getAnnotationBeanId(clazz);
                    Class<?>[] interfaces = clazz.getInterfaces();
                    Object obj = clazz.newInstance();
                    //得到代理类
                    ProxyFactory proxyFactory = getBean(ProxyFactory.class);
                    if (interfaces.length == 0) {
                        //cglib代理
                        Object proxy = proxyFactory.getCglibProxy(object);
                        beanMap.put(beanId, proxy);
                    } else {
                        // jdk代理
                        Object proxy = proxyFactory.getJdkProxy(object);
                        beanMap.put(beanId, proxy);
                    }
                }
            }
        }
    }


}
